﻿using System;
using System.Activities;
using System.Activities.Statements;
using System.Activities.Validation;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.Text;
using System.Threading.Tasks;

namespace WFExtended.Framework.Constraints
{
    /// <summary>
    /// Activity Constraint for check Service Contract
    /// </summary>
    /// <typeparam name="A">Type of the activity to check</typeparam>
    public class ServiceConstraint<A> : AConstraint<A>
        where A : Activity
    {
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="isWarning">Define if the constraint is a warning</param>
        /// <param name="propertyName">Contraint property name</param>
        /// <param name="serviceType">Service type</param>
        public ServiceConstraint(bool isWarning, string propertyName, Type serviceType)
            : base(isWarning, propertyName)
        {
            _serviceType = serviceType;
        }

        private Type _serviceType;

        /// <summary>
        /// Get the activity Constraint
        /// </summary>
        /// <returns>Return the activity Constraint</returns>
        public override System.Activities.Validation.Constraint<A> GetConstraint()
        {
            var currentActivity = new DelegateInArgument<A>();
            var validationContext = new DelegateInArgument<ValidationContext>();
            var errorMessage = new Variable<string>();

            return new Constraint<A>
           {
               Body = new ActivityAction<A, ValidationContext>
               {
                   Argument1 = currentActivity,
                   Argument2 = validationContext,

                   Handler = new Sequence
                   {
                       Variables =
                        {
                            errorMessage
                        },
                       Activities =
                        {
                            new Assign<string>
                            {
                                To = errorMessage,
                                Value = new InArgument<string>((context)=> ValidateService())
                            },
                            new AssertValidation() 
                            {
                                Assertion = new InArgument<bool>((context) => String.IsNullOrEmpty(errorMessage.Get(context))),
                                IsWarning = IsWarning,
                                Message = errorMessage,
                                PropertyName = PropertyName
                            }
                        }
                   }
               }
           };

        }

        private string ValidateService()
        {
            string errorMessage = String.Empty;

            ServiceContractAttribute serviceAttr =
               _serviceType.GetCustomAttributes(typeof(ServiceContractAttribute), true).FirstOrDefault() as ServiceContractAttribute;

            if (serviceAttr == null)
            {
                return String.Format("Type {0} is not a Service Contract. Set the attribute ServiceContractAttribute to your type {0}", _serviceType.Name);
            }

            if (String.IsNullOrEmpty(serviceAttr.Name))
                serviceAttr.Name = _serviceType.Name;

            if (_serviceType.GetMethods()
                .Count((M) => M.GetCustomAttributes(typeof(OperationContractAttribute), true).Length > 0) == 0)
                return String.Format("The Service Contract {0} has no OperationContract Method", serviceAttr.Name);

            return String.Empty;
        }
    }
}
